Skip to main content

Webpack 5 & Modern Build Tools Cheatsheet

Table of Contentsโ€‹

  1. Build Tools Overview
  2. Why Build Tools Are Needed
  3. Webpack 5
  4. Vite
  5. Why Vite is Faster
  6. Other Modern Build Tools
  7. Performance Comparison
  8. When to Use What
  9. Migration Guide
  10. Interview Questions

Build Tools Overviewโ€‹

What Are Build Tools?โ€‹

Build tools transform and bundle source code into optimized production assets.

Source Code               Build Tool              Production
โ”œโ”€โ”€ TypeScript โ†’ โ†’ โ”œโ”€โ”€ Minified JS
โ”œโ”€โ”€ JSX/TSX โ†’ Webpack/Vite โ†’ โ”œโ”€โ”€ CSS bundles
โ”œโ”€โ”€ SASS/LESS โ†’ โ†’ โ”œโ”€โ”€ Optimized images
โ”œโ”€โ”€ Modern JS โ†’ โ†’ โ””โ”€โ”€ index.html
โ””โ”€โ”€ Assets
ToolTypeReleasePrimary Use Case
WebpackBundler2012Production apps, complex configs
ViteDev Server + Bundler2020Fast dev, modern projects
RollupBundler2015Libraries, tree-shaking
ParcelZero-config bundler2017Quick prototypes
esbuildBundler2020Extremely fast builds
TurbopackBundler2022Next.js, Rust-based
SWCCompiler2020Faster Babel alternative

Why Build Tools Are Neededโ€‹

Key Problems They Solveโ€‹

1. Module Systemโ€‹

Browsers don't natively support CommonJS/ES Modules everywhere.

// Won't work directly in older browsers
import React from 'react';
const lodash = require('lodash');

2. Code Transformationโ€‹

Transform modern syntax to browser-compatible code.

// JSX โ†’ JavaScript
<div className="app">Hello</div>
// โ†“
React.createElement('div', { className: 'app' }, 'Hello')

3. Asset Processingโ€‹

Handle CSS, images, fonts, etc.

import './styles.css';
import logo from './logo.png';

4. Code Optimizationโ€‹

  • Minification
  • Tree shaking (remove unused code)
  • Code splitting
  • Dead code elimination

5. Developer Experienceโ€‹

  • Hot Module Replacement (HMR)
  • Source maps
  • TypeScript support
  • Fast refresh

Webpack 5โ€‹

Core Conceptsโ€‹

// webpack.config.js
module.exports = {
// 1. Entry: Starting point
entry: './src/index.js',

// 2. Output: Where to emit bundles
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
},

// 3. Loaders: Transform files
module: {
rules: [
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /node_modules/,
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif)$/,
type: 'asset/resource',
},
],
},

// 4. Plugins: Additional functionality
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new MiniCssExtractPlugin(),
],

// 5. Mode: development or production
mode: 'production',
};

How Webpack Worksโ€‹

1. Entry Point
โ†“
2. Build Dependency Graph (parse all imports)
โ†“
3. Apply Loaders (transform files)
โ†“
4. Apply Plugins (optimize, emit assets)
โ†“
5. Output Bundle(s)

Webpack 5 Key Featuresโ€‹

1. Persistent Cachingโ€‹

module.exports = {
cache: {
type: 'filesystem', // Cache to disk
buildDependencies: {
config: [__filename],
},
},
};

2. Module Federationโ€‹

Share code between separate builds (microfrontends).

// App 1 (host)
new ModuleFederationPlugin({
name: 'host',
remotes: {
app2: 'app2@http://localhost:3002/remoteEntry.js',
},
});

// App 2 (remote)
new ModuleFederationPlugin({
name: 'app2',
filename: 'remoteEntry.js',
exposes: {
'./Button': './src/Button',
},
});

// Use in App 1
import Button from 'app2/Button';

3. Asset Modulesโ€‹

No need for file-loader, url-loader, raw-loader.

module.exports = {
module: {
rules: [
{
test: /\.png$/,
type: 'asset/resource', // Emits separate file
},
{
test: /\.svg$/,
type: 'asset/inline', // Inline as data URI
},
{
test: /\.txt$/,
type: 'asset/source', // Export as string
},
{
test: /\.jpg$/,
type: 'asset', // Auto choose based on size
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8kb
},
},
},
],
},
};

4. Tree Shaking Improvementsโ€‹

Better detection of unused exports.

// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b; // Won't be bundled if unused

// app.js
import { add } from './utils';
console.log(add(2, 3));

5. Code Splittingโ€‹

// Dynamic imports
const loadComponent = async () => {
const module = await import(/* webpackChunkName: "heavy" */ './HeavyComponent');
return module.default;
};

// Split vendors
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10,
},
},
},
}

Webpack Dev Serverโ€‹

devServer: {
port: 3000,
hot: true, // Hot Module Replacement
open: true,
historyApiFallback: true, // SPA routing
proxy: {
'/api': 'http://localhost:8080',
},
}

Viteโ€‹

What is Vite?โ€‹

Fast dev server + production bundler (uses Rollup) built on native ES modules.

Basic Setupโ€‹

// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [react()],
server: {
port: 3000,
},
build: {
outDir: 'dist',
sourcemap: true,
},
});

How Vite Worksโ€‹

Development Modeโ€‹

Browser Request
โ†“
Vite Dev Server
โ†“
Transform on-demand (esbuild)
โ†“
Serve ES Modules

Key Difference: No bundling in dev! Each file is served separately.

<!-- Vite serves this directly -->
<script type="module">
import { createApp } from '/node_modules/vue/dist/vue.esm-browser.js'
import App from '/src/App.vue'
createApp(App).mount('#app')
</script>

Production Modeโ€‹

Uses Rollup for optimized bundling.

Source Files
โ†“
Rollup (bundler)
โ†“
Optimized Bundle

Vite Featuresโ€‹

1. Instant Server Startโ€‹

# Webpack: 10-30s for large projects
npm run webpack-dev

# Vite: <1s regardless of project size
npm run vite

2. Lightning Fast HMRโ€‹

// Vite only updates the changed module
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Handle update
});
}

3. Optimized Dependenciesโ€‹

Vite pre-bundles dependencies with esbuild.

node_modules/
โ”œโ”€โ”€ react (100+ files)
โ”œโ”€โ”€ lodash (500+ files)
โ†“ (Pre-bundled)
.vite/deps/
โ”œโ”€โ”€ react.js (1 file)
โ””โ”€โ”€ lodash.js (1 file)

4. Built-in Featuresโ€‹

No loaders needed for common files:

// TypeScript
import Component from './Component.tsx';

// CSS
import './styles.css';

// CSS Modules
import styles from './App.module.css';

// JSON
import data from './data.json';

// Assets
import logo from './logo.png';

// Web Workers
import Worker from './worker?worker';

5. Environment Variablesโ€‹

// .env
VITE_API_URL=https://api.example.com

// Access in code
console.log(import.meta.env.VITE_API_URL);

Why Vite is Fasterโ€‹

1. No Bundling in Developmentโ€‹

Webpack Dev:
[All Files] โ†’ Bundle โ†’ Serve (slow start)

Vite Dev:
[Request] โ†’ Transform Single File โ†’ Serve (instant)

2. Native ES Modulesโ€‹

Modern browsers support ES modules natively.

// Browser can handle this directly
import { useState } from 'react';

3. esbuild for Dependenciesโ€‹

esbuild is written in Go (10-100x faster than JavaScript-based tools).

Babel Transform: ~1000ms
esbuild Transform: ~10ms

4. Smart Dependency Pre-bundlingโ€‹

// These are pre-bundled once
import _ from 'lodash'; // 100s of files โ†’ 1 file
import React from 'react'; // Multiple files โ†’ 1 file

// These are transformed on-demand
import App from './App.jsx'; // Your code

5. Optimized HMRโ€‹

Webpack HMR:
Change โ†’ Rebuild affected chunks โ†’ Full page update

Vite HMR:
Change โ†’ Transform single module โ†’ Precise update

6. Better Cachingโ€‹

// Strong caching with immutable filenames
// dist/assets/index-4f3b8d2a.js
// โ†“ Change code
// dist/assets/index-8a2c9f1b.js (new hash)

Performance Numbersโ€‹

Cold Server Start:
Webpack: 15-30s (large project)
Vite: <1s

Hot Module Replacement:
Webpack: 200-500ms
Vite: <50ms

Production Build:
Webpack: 30-60s
Vite: 20-40s (uses Rollup)

Other Modern Build Toolsโ€‹

esbuildโ€‹

Extremely fast bundler written in Go.

// esbuild.config.js
require('esbuild').build({
entryPoints: ['src/index.js'],
bundle: true,
outfile: 'dist/bundle.js',
minify: true,
});

Speed: 10-100x faster than webpack Limitation: Less plugin ecosystem

Turbopack (Next.js)โ€‹

Rust-based bundler by Vercel.

// next.config.js
module.exports = {
experimental: {
turbo: {}, // Enable Turbopack
},
};

Claims: 700x faster than Webpack, 10x faster than Vite (disputed)

Rollupโ€‹

Tree-shaking focused bundler, great for libraries.

// rollup.config.js
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
},
plugins: [resolve(), commonjs(), terser()],
};

Parcelโ€‹

Zero-config bundler.

# No config needed
parcel index.html

Pros: Easy to use Cons: Less control, slower than modern tools

SWCโ€‹

Rust-based JavaScript/TypeScript compiler (Babel alternative).

// .swcrc
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}

Speed: 20x faster than Babel Used by: Next.js, Vite (optionally)


Performance Comparisonโ€‹

Build Speed (10,000 modules)โ€‹

Tool           Cold Start    Hot Reload    Production Build
โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Webpack 5 ~25s ~300ms ~45s
Vite <1s ~50ms ~30s
esbuild <500ms N/A ~5s
Turbopack ~1s ~30ms ~25s

Why Speed Differences?โ€‹

Language Implementation:
JavaScript (Webpack): Slow
Go (esbuild): Fast (10-100x)
Rust (Turbopack, SWC): Fast (10-100x)

Architecture:
Bundle-first (Webpack): Slow dev start
Module-first (Vite): Fast dev start

When to Use Whatโ€‹

Use Webpack When:โ€‹

โœ… You need Module Federation โœ… Complex custom configurations required โœ… Legacy project migration โœ… Need specific loaders/plugins โœ… Team is already familiar with it

// Complex webpack use case
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js',
},
optimization: {
runtimeChunk: 'single',
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
};

Use Vite When:โ€‹

โœ… Starting a new project โœ… Developer experience is priority โœ… Modern browser targets โœ… TypeScript/JSX/CSS modules โœ… Fast iteration needed

// Simple Vite setup
export default defineConfig({
plugins: [react()],
// That's it for most projects!
});

Use esbuild When:โ€‹

โœ… Building libraries โœ… CI/CD pipelines (speed critical) โœ… Simple bundling needs โœ… No complex transformations

Use Rollup When:โ€‹

โœ… Building npm packages โœ… Tree-shaking is critical โœ… Multiple output formats needed

// Library with multiple formats
export default {
input: 'src/index.js',
output: [
{ file: 'dist/bundle.cjs.js', format: 'cjs' },
{ file: 'dist/bundle.esm.js', format: 'esm' },
{ file: 'dist/bundle.umd.js', format: 'umd', name: 'MyLib' },
],
};

Migration Guideโ€‹

Webpack to Viteโ€‹

// webpack.config.js
module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.jsx?$/, use: 'babel-loader' },
],
},
};

// โ†“ Migrate to โ†“

// vite.config.js
export default defineConfig({
plugins: [react()],
// CSS and JSX work out of the box!
});

Key Changesโ€‹

// Webpack: CommonJS
const path = require('path');
module.exports = { /* ... */ };

// Vite: ES Modules
import { defineConfig } from 'vite';
export default defineConfig({ /* ... */ });

// Webpack: Environment variables
process.env.REACT_APP_API_URL

// Vite: Environment variables
import.meta.env.VITE_API_URL

// Webpack: Import assets
import logo from './logo.png';

// Vite: Import assets (same, but different handling)
import logo from './logo.png'; // Returns URL
import logo from './logo.png?url'; // Explicit URL
import logo from './logo.png?raw'; // Raw string

Migration Checklistโ€‹

  • Update package.json scripts
  • Convert webpack config to vite config
  • Update environment variable names (REACT_APP_* โ†’ VITE_*)
  • Remove webpack-specific loaders
  • Update import statements if using CommonJS
  • Test all dynamic imports
  • Update proxy configuration
  • Test production build

Interview Questionsโ€‹

1. What is tree shaking?โ€‹

Removing unused code from bundles. Works with ES modules (static analysis).

// lib.js
export function used() { return 'used'; }
export function unused() { return 'unused'; }

// app.js
import { used } from './lib';
console.log(used());

// Bundle: Only includes 'used' function

2. What is code splitting?โ€‹

Breaking code into separate bundles loaded on-demand.

// Static
import Component from './Component';

// Dynamic (creates separate chunk)
const Component = lazy(() => import('./Component'));

3. How does HMR work?โ€‹

Hot Module Replacement updates modules without full page reload.

File Change โ†’ Dev Server Detects โ†’ WebSocket Message โ†’ Update Module

4. Why is Vite faster than Webpack in development?โ€‹

  1. No bundling (uses native ES modules)
  2. On-demand transformation (only requested files)
  3. esbuild for dependencies (Go vs JavaScript)
  4. Optimized dependency pre-bundling
  5. Better caching strategy

5. What is the difference between loader and plugin in Webpack?โ€‹

Loaders: Transform individual files (CSS, TS, images) Plugins: Perform broader tasks (optimization, injection, bundling)

module: {
rules: [
{ test: /\.css$/, use: 'css-loader' }, // Loader
],
},
plugins: [
new HtmlWebpackPlugin(), // Plugin
],

6. Explain Module Federationโ€‹

Share code between separate Webpack builds at runtime.

// Use case: Microfrontends
// App A can import components from App B
// without rebuilding or duplicating code

7. What are source maps?โ€‹

Map minified/transformed code back to original source for debugging.

devtool: 'source-map', // webpack
build: { sourcemap: true }, // vite

8. How does Vite handle dependencies?โ€‹

Pre-bundles with esbuild, caches aggressively, serves as single files.

First run: Scan โ†’ Pre-bundle โ†’ Cache
Subsequent: Serve from cache (instant)

9. What is the purpose of content hashing?โ€‹

Cache busting - new hash when content changes.

output: {
filename: '[name].[contenthash].js', // webpack
}

// Produces: main.4f3b8d2a.js
// Change content โ†’ main.8a2c9f1b.js

10. When would you choose Webpack over Vite?โ€‹

  • Module Federation needed
  • Complex existing configuration
  • Required plugins only available in Webpack
  • Server-side rendering with custom setup
  • Need fine-grained control over bundling

Quick Referenceโ€‹

Webpack vs Vite Comparisonโ€‹

FeatureWebpack 5Vite
Dev StartSlow (bundle first)Instant (no bundle)
HMR~300ms~50ms
ConfigComplexSimple
PluginsExtensiveGrowing
ProductionWebpackRollup
Browser SupportAllModern (ES2015+)
Learning CurveSteepGentle

Common Commandsโ€‹

# Webpack
webpack --mode production
webpack serve --mode development

# Vite
vite build
vite dev
vite preview

# Package.json scripts
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}

Key Takeawaysโ€‹

โœ… Webpack: Mature, powerful, configurable, slower dev experience โœ… Vite: Fast, modern, simple, best for new projects โœ… esbuild: Fastest builds, limited ecosystem โœ… Choice depends on: Project needs, team familiarity, browser support